#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#include "sys/socket.h"
#include "sys/types.h"
#include "netinet/in.h"

#include "unistd.h"
#include "fcntl.h"


#define SEVICE_PORT		8888
#define MAX_LISTEN_QUE	5
#define MAX_RX_BUFF		100
#define MAX_TX_BUFF		100

void error_die(const char *str)
{
	perror(str);
	exit(-1);
}

int create_socket(void)
{
	int listen_fd;
	int ret, opt = 1;
	socklen_t len;
	struct sockaddr_in server;
	

	listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(listen_fd < 0)
		error_die("socket error");

	ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if(ret < 0)
		error_die("setsockopt error");

	memset(&server, 0, sizeof(struct sockaddr_in));
	//bzero(&server, sizeof(server));
	server.sin_family 			= AF_INET;
	server.sin_addr.s_addr 		= htonl(INADDR_ANY);
	server.sin_port				= htons(SEVICE_PORT);
	len							= sizeof(struct sockaddr);
	ret = bind(listen_fd, (struct sockaddr *)&server, len);
	if(ret < 0)
		error_die("bind error");

	listen(listen_fd, MAX_LISTEN_QUE);

	return listen_fd;
}


int main(int argc, char *argv[])
{
	int listen_fd;
	int nfds;
	int sockfd;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	socklen_t server_len;
	socklen_t client_len = sizeof(struct sockaddr_in);
	int ret;
	int rdfd_idx;
	int bytes = 0;
	fd_set global_rd_fds;
	fd_set current_rd_fds;
	char rx[MAX_RX_BUFF];
	char tx[MAX_TX_BUFF];

	/* 1. create listen fd */
	listen_fd = create_socket();

	/* prepare fd_set and nfds for select() */
	FD_ZERO(&global_rd_fds);
	FD_SET(listen_fd, &global_rd_fds);
	nfds = listen_fd;

	while(1)
	{
		current_rd_fds = global_rd_fds;
		/* 	2. while() block at this point, 
		*	if there is read_fd is ready for read
		*	while() continue
		*/
		ret = select(nfds + 1, &current_rd_fds, NULL, NULL, NULL);
		if(ret < 0)
			error_die("select error");

		for(rdfd_idx = 0; rdfd_idx <= nfds; rdfd_idx++)
		{
			/* 3. poll check fds */
			if(FD_ISSET(rdfd_idx, &current_rd_fds)){

				/* 3.1 if listen_fd is set
				* 		call accept to create a communicate sock
				*/
				if(rdfd_idx == listen_fd){
					sockfd = accept(listen_fd, (struct sockaddr *)&client_addr, (socklen_t *)&client_len);
					if(sockfd < 0)
						error_die("accept error");
					/* rm listen_fd from rd_fds */
					printf("new sock fd: %d\n", sockfd);
					FD_CLR(rdfd_idx, &current_rd_fds);
					/* nfds must be modified, because we create a new comm sock */
					nfds = nfds > sockfd ? nfds :sockfd;
					/* new comm sock_fd add to rd_fds to select*/
					FD_SET(sockfd, &global_rd_fds);
				}else{
					/*
						3.2 if comm sock_fd is set
						read_routine is to be done
					*/
					printf("read socket: %d\n", rdfd_idx);
					bytes = recv(rdfd_idx, rx, MAX_RX_BUFF, 0);
					if(bytes < 0)
						error_die("recv error");
					if(bytes == 0){
						FD_CLR(rdfd_idx, &current_rd_fds);
						close(rdfd_idx);
						continue;
					}

					printf("rx data: %s\n", rx);
					send(rdfd_idx, rx, bytes, 0);
				}
			}
		}
	}
}
